/*
#Copyright (c) 2013, Philippe Bordron <philippe.bordron@gmail.com>
#
# This file is part of SIPPER.
#
# DCC is free software: you can redistribute it and/or modify
# it under the terms of the GNU LESSER GENERAL PUBLIC LICENSE as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DCC is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU LESSER GENERAL PUBLIC LICENSE for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# along with SIPPER.  If not, see <http://www.gnu.org/licenses/>
*/
package combi.wip.sipper.modeling.reactions;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import combi.wip.sipper.modeling.reactions.ReactionUtil.Orientation;

public class Reaction<C> {

	protected String id;
	protected Set<C> leftPart;
	protected Set<C> rightPart;
	protected ReactionUtil.Orientation direction;

	public Reaction(String id, Collection<C> leftPart, Collection<C> rightPart, ReactionUtil.Orientation direction)
	{
		this.id = id;
		this.leftPart = new HashSet<C>(leftPart);
		this.rightPart = new HashSet<C>(rightPart);
		this.direction = direction;
		normalize();
	}

	public Reaction(Reaction<C> r) {
		this.id = r.id;
		this.leftPart = new HashSet<C>(r.leftPart);
		this.rightPart = new HashSet<C>(r.rightPart);
		this.direction = r.direction;
		normalize();
	}

	private void normalize()
	{
		Set<C> tmp;
		if (direction == ReactionUtil.Orientation.RL)
		{
			tmp = this.leftPart;
			this.leftPart = this.rightPart;
			this.rightPart = tmp;
			direction = ReactionUtil.Orientation.LR;
		}
	}


	public String getId() {
		return id;
	}

	public Set<C> getLeftPart() {
		return leftPart;
	}

	public Set<C> getRightPart() {
		return rightPart;
	}

	public ReactionUtil.Orientation getDirection() {
		return direction;
	}


	public void setOrientation(ReactionUtil.Orientation direction)
	{
		this.direction = direction;
	}
	
	public Set<C> getCompounds(){
		Set<C> result = new HashSet<C>();
		result.addAll(this.getLeftPart());
		result.addAll(this.getRightPart());
		return result;
	}

	public Set<C> getSubstrats() {
		switch(direction){
		case LR :
			return new HashSet<C>(this.getLeftPart());
		case RL :
			return new HashSet<C>(this.getRightPart());
		default : 
			return this.getCompounds();
		}
	}

	public Set<C> getProducts() {
		switch(direction){
		case LR :
			return new HashSet<C>(this.getRightPart());
		case RL :
			return new HashSet<C>(this.getLeftPart());
		default : 
			return this.getCompounds();
		}
	}

	private Set<C> intersect(Set<C> s1, Set<C> s2)
	{
		Set<C> tmp = new HashSet<C>(s1);
		tmp.retainAll(s2);
		return tmp;
	}

	private boolean setsEqual(Set<C> s1, Set<C> s2)
	{
		Set<C> tmp = intersect(s1, s2);
		return s1.size() == tmp.size();
	}

	public boolean isSimilaire(Reaction<C> other)
	{
		Set<C> pg = this.getLeftPart(), pd = this.getRightPart(),
				opg = other.getLeftPart(), opd = other.getRightPart();
		Orientation d=this.getDirection(), od=other.getDirection();
		if((d==Orientation.LR && od == Orientation.LR)||(d==Orientation.RL && od == Orientation.RL))
		{
			return setsEqual(pg,opg) && setsEqual(pd,opd);
		}
		if((d==Orientation.LR && od == Orientation.RL)||(d==Orientation.RL && od == Orientation.LR))
		{
			return setsEqual(pd,opg) && setsEqual(pg,opd);
		}
		if(d==Orientation.BOTH || d==Orientation.UNDEFINED)
		{
			if(od==Orientation.BOTH || od==Orientation.UNDEFINED)
			{
				return 	(setsEqual(pd,opg) && setsEqual(pg,opd)) || (setsEqual(pd,opg) && setsEqual(pg,opd));
			}
		}
		return false;//sinon pas meme orientation donc distinct
	}

	public boolean isValid()
	{
		return !this.getLeftPart().isEmpty() && !this.getRightPart().isEmpty();
	}


	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Reaction<?> other = (Reaction<?>) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

	@Override
	public int hashCode() {
		return id.hashCode();
	}

	public String toString()
	{
		String orientation = "<->";
		switch (this.getDirection())
		{
		case LR : orientation = "->";
		break;
		case RL : orientation = "<-";
		break;
		default:
		}
		return (this.getId()+ ": " + this.getLeftPart() +" "+ orientation +" " + this.getRightPart()).toString().replaceAll(","," +").replaceAll("[\\[\\]]","");
	}
}
